package org.example.ajintegration.proxy;

import okhttp3.*;
import org.example.ajintegration.config.AJReportConfig;
import org.example.ajintegration.exception.AJReportConnectionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class AJReportProxy {
    
    @Autowired
    private AJReportConfig config;
    
    private String token;
    private long tokenExpireTime;
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build();
    
    private ScheduledExecutorService scheduler;
    
    @PostConstruct
    public void init() {
        // 启动定时器，检查 token 是否需要更新
        scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(this::checkAndRefreshToken, 60, 60, TimeUnit.SECONDS);
        
        // 初始化时获取 token
        try {
            updateToken();
        } catch (Exception e) {
            // 初始化失败，记录日志但不抛出异常
            System.err.println("初始化 AJReport 代理失败: " + e.getMessage());
        }
    }
    
    @PreDestroy
    public void destroy() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
            }
        }
    }
    
    /**
     * 检查并刷新 token
     */
    private void checkAndRefreshToken() {
        try {
            long currentTime = System.currentTimeMillis();
            long timeUntilExpire = tokenExpireTime - currentTime;
            
            // 如果距离过期时间小于阈值，则更新 token
            if (timeUntilExpire < config.getTokenRefreshThresholdSeconds() * 1000L) {
                updateToken();
            }
        } catch (Exception e) {
            System.err.println("Token 自动更新失败: " + e.getMessage());
        }
    }
    
    /**
     * 更新 token（同步方法，确保线程安全）
     */
    public synchronized void updateToken() throws IOException {
        MediaType mediaType = MediaType.parse("application/json");
        String requestBody = objectMapper.writeValueAsString(Map.of(
            "loginName", config.getLoginName(),
            "password", config.getPassword()
        ));
        
        RequestBody body = RequestBody.create(mediaType, requestBody);
        Request request = new Request.Builder()
                .url(config.getUrl() + "/accessUser/login")
                .post(body)
                .addHeader("Content-Type", "application/json")
                .build();
        
        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new AJReportConnectionException("HTTP 请求失败，状态码: " + response.code());
            }
            
            String responseBody = response.body().string();
            JsonNode jsonNode = objectMapper.readTree(responseBody);
            String code = jsonNode.get("code").asText();
            
            if (!"200".equals(code)) {
                throw new AJReportConnectionException("登录失败，响应码: " + code + ", 消息: " + jsonNode.get("message").asText());
            }
            
            // 从响应中提取 token
            JsonNode dataNode = jsonNode.get("data");
            if (dataNode != null && dataNode.has("token")) {
                token = dataNode.get("token").asText();
                // 设置 token 过期时间
                tokenExpireTime = System.currentTimeMillis() + config.getTokenExpireSeconds() * 1000L;
            } else {
                throw new AJReportConnectionException("响应中未找到 token");
            }
        } catch (Exception e) {
            if (e instanceof AJReportConnectionException) {
                throw e;
            }
            throw new AJReportConnectionException("更新 token 失败", e);
        }
    }
    
    /**
     * 确保 token 有效
     */
    private void ensureTokenValid() {
        // 如果 token 不存在或已过期，则更新
        if (token == null || System.currentTimeMillis() >= tokenExpireTime) {
            try {
                updateToken();
            } catch (IOException e) {
                throw new AJReportConnectionException("无法获取有效的 token", e);
            }
        }
    }
    
    /**
     * 处理 token 过期响应
     */
    private boolean handleTokenExpiredResponse(String responseBody) {
        try {
            JsonNode jsonNode = objectMapper.readTree(responseBody);
            String code = jsonNode.get("code").asText();
            if ("User.credentials.expired".equals(code)) {
                // token 过期，更新后再重试
                updateToken();
                return true;
            }
        } catch (Exception e) {
            // 解析失败，不是 token 过期响应
        }
        return false;
    }
    
    /**
     * GET 请求代理方法
     * @param path 请求路径
     * @param params 查询参数
     * @return 响应内容
     */
    public String proxiedGetMethod(String path, HashMap<String, String> params) throws IOException {
        ensureTokenValid();
        
        // 构建带参数的 URL
        String url = config.getUrl() + path;
        if (params != null && !params.isEmpty()) {
            String queryString = params.entrySet().stream()
                    .map(entry -> entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.joining("&"));
            url += "?" + queryString;
        }
        
        Request request = new Request.Builder()
                .url(url)
                .get()
                .addHeader("Authorization", token)
                .addHeader("Content-Type", "application/json")
                .build();
        
        try (Response response = httpClient.newCall(request).execute()) {
            String responseBody = response.body().string();
            
            // 检查是否是 token 过期响应
            if (handleTokenExpiredResponse(responseBody)) {
                // 重新构建请求并重试
                request = new Request.Builder()
                        .url(url)
                        .get()
                        .addHeader("Authorization", token)
                        .addHeader("Content-Type", "application/json")
                        .build();
                
                try (Response retryResponse = httpClient.newCall(request).execute()) {
                    return retryResponse.body().string();
                }
            }
            
            return responseBody;
        }
    }
    
    /**
     * POST 请求代理方法
     * @param path 请求路径
     * @param bodyParams 请求体参数
     * @return 响应内容
     */
    public String proxiedPostMethod(String path, HashMap<String, Object> bodyParams) throws IOException {
        ensureTokenValid();
        
        MediaType mediaType = MediaType.parse("application/json");
        String requestBody = objectMapper.writeValueAsString(bodyParams);
        RequestBody body = RequestBody.create(mediaType, requestBody);
        
        Request request = new Request.Builder()
                .url(config.getUrl() + path)
                .post(body)
                .addHeader("Authorization", token)
                .addHeader("Content-Type", "application/json")
                .build();
        
        try (Response response = httpClient.newCall(request).execute()) {
            String responseBody = response.body().string();
            
            // 检查是否是 token 过期响应
            if (handleTokenExpiredResponse(responseBody)) {
                // 重新构建请求并重试
                body = RequestBody.create(mediaType, requestBody);
                request = new Request.Builder()
                        .url(config.getUrl() + path)
                        .post(body)
                        .addHeader("Authorization", token)
                        .addHeader("Content-Type", "application/json")
                        .build();
                
                try (Response retryResponse = httpClient.newCall(request).execute()) {
                    return retryResponse.body().string();
                }
            }
            
            return responseBody;
        }
    }
    
    /**
     * 获取当前 token（用于测试）
     */
    public String getToken() {
        return token;
    }
    
    /**
     * 获取 token 过期时间（用于测试）
     */
    public long getTokenExpireTime() {
        return tokenExpireTime;
    }
}
